package com.ants.gateway.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import org.springframework.security.core.GrantedAuthority;
import java.util.*;

/**
 * @author 蚂蚁会花呗
 * @create 2025/5/28 15:19
 */
@Component
public class RestfulAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    Logger logger = LoggerFactory.getLogger(RestfulAuthorizationManager.class);

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    private static final String GATEWAY_RESOURCE_ROLE_KEY = "gateway:resource";

    /**
     * 此处保存的是资源对应的权限，可以从数据库中获取
     */
/*    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        Object obj = redisTemplate.opsForList().range(GATEWAY_RESOURCE_ROLE_KEY,0,-1);
        // 不在权限范围内的url，全部拒绝
        if (obj == null) {
            return Mono.just(new AuthorizationDecision(false));
        }
        String redisMapStr = JSON.toJSONString(obj);
        List<Object> list = JSON.parseArray(redisMapStr);
        List<String> authorities= new ArrayList<>();
        for (Object o : list){
            Map<String,Object> map = JSONObject.parseObject(JSON.toJSONString(o));
            for (Map.Entry<String,Object> entity: map.entrySet()){
                System.out.println(entity.getKey());
                System.out.println(entity.getValue());
                //带通配符的可以使用这个进行匹配
                if (checkPermit(path,entity.getKey())){
                    authorities = JSON.parseArray(JSON.toJSONString(entity.getValue()),String.class);
                }
            }
        }
        logger.info("访问路径:[{}],所需要的权限是:[{}]", path, authorities);
        // option 请求，全部放行
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }
        List<String> finalAuthorities = authorities;
        return authentication
                .filter(a -> a instanceof JwtAuthenticationToken)
                .cast(JwtAuthenticationToken.class)
                .doOnNext(token -> {
                    System.out.println("headers:" + token.getToken().getHeaders());
                    System.out.println("token:" + token.getTokenAttributes());
                })
                .map(auth -> {
            return new AuthorizationDecision(checkAuthorities(exchange,auth, finalAuthorities));
        }).defaultIfEmpty(new AuthorizationDecision(false));
    }*/

    /**
     * 校验权限
     * @param exchange
     * @param auth
     * @param roles
     * @return
     */
    private boolean checkAuthorities(ServerWebExchange exchange,Authentication auth,List<String> roles ){
        if (auth instanceof JwtAuthenticationToken){
            for (String role: roles){
                for (GrantedAuthority authRole:auth.getAuthorities()){
                    if (role.equals(authRole.getAuthority())){
                        return true;
                    }
                }
            }
         }
        return false;
    }

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        Object obj = redisTemplate.opsForList().range(GATEWAY_RESOURCE_ROLE_KEY,0,-1);
        // 不在权限范围内的url，全部拒绝
        if (obj == null) {
            return Mono.just(new AuthorizationDecision(false));
        }
        String redisMapStr = JSON.toJSONString(obj);
        List<Object> list = JSON.parseArray(redisMapStr);
        List<String> authorities= new ArrayList<>();
        for (Object o : list){
            Map<String,Object> map = JSONObject.parseObject(JSON.toJSONString(o));
            for (Map.Entry<String,Object> entity: map.entrySet()){
                //带通配符的可以使用这个进行匹配
                if (checkPermit(path,entity.getKey())){
                    authorities = JSON.parseArray(JSON.toJSONString(entity.getValue()),String.class);
                }
            }
        }
        logger.info("访问路径:[{}],所需要的权限是:[{}]", path, authorities);
        // option 请求，全部放行
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }
        List<String> finalAuthorities = authorities;
        return authentication
                .filter(Authentication::isAuthenticated)
                .filter(a -> a instanceof JwtAuthenticationToken)
                .cast(JwtAuthenticationToken.class)
                .doOnNext(token -> {
                    System.out.println(token.getToken().getHeaders());
                    System.out.println(token.getTokenAttributes());
                })
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(finalAuthorities::contains)
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

    /**
     * 通过通配符校验
     * @param path
     * @return
     */
    private boolean checkPermit(String path,String redisPath) {
        AntPathMatcher pathMatcher = new AntPathMatcher();
        return pathMatcher.match(redisPath,path);
    }


}
